package basic

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	name, hobby string
	age         int
	Address
}

type Address struct {
	Province string
	city     string
}

//Student 学生
type Student struct {
	ID     int
	Gender string
	Name   string
}

//Class 班级
type Class struct {
	Title    string
	Students []Student
}

func Struct() {
	//实例化

	//var
	var a Person
	a.name = "蔡鹏鹏"

	//new
	var c = new(Person)
	c.name = "cs"
	var d = &Person{}
	d.name = "cs1"
	d.city = "12321"

	//匿名
	var b struct{ name string }
	b.name = "cpp"

	//初始化
	person := Person{
		name:  "蔡鹏鹏",
		hobby: "爱兴趣",
		age:   18,
	}
	fmt.Print(a, b, c, d, d.Address.city, person)

	//todo 结构体占用一块连续的内存

	//构造函数
	fmt.Print(getPerson("cpp11", 28))

	//方法和接受者
	// func(接受者变量 接受者类型) 方法名 (参数) 返回值{}
	//指针类型的接收者
	//指针类型的接收者由一个结构体的指针组成，由于指针的特性，调用方法时修改接收者指针的任意成员变量，在方法结束后，修改都是有效的。
	//这种方式就十分接近于其他语言中面向对象中的this或者self。 例如我们为Person添加一个SetAge方法，来修改实例变量的年龄。
	//值类型的接收者
	//当方法作用于值类型接收者时，Go语言会在代码运行时将接收者的值复制一份。
	//在值类型接收者的方法中可以获取接收者的成员值，但修改操作只是针对副本，无法修改接收者变量本身。

	//什么时候应该使用指针类型接收者
	//需要修改接收者中的值
	//接收者是拷贝代价比较大的大对象
	//保证一致性，如果有某个方法使用了指针接收者，那么其他的方法也应该使用指针接收者。

	//嵌套结构体
	//一个结构体中可以嵌套包含另一个结构体或结构体指针，

	//继承
	dog := Dog{
		Feet: 4,
		Animal: Animal{
			name: "cpp",
		},
	}
	dog.move()
	dog.wang()
	fmt.Printf("type of dog:%T\n", dog)
	fmt.Print(111, dog, 111)

	//结构体字段的可见性
	//结构体中字段大写开头表示可公开访问，小写表示私有（仅在定义当前结构体的包中可访问）。

	//结构体与JSON序列化
	class := &Class{
		Title:    "三年一班",
		Students: make([]Student, 0, 200),
	}

	for i := 0; i < 10; i++ {
		class.Students = append(class.Students, Student{
			ID:     1,
			Gender: "男",
			Name:   "蔡鹏鹏",
		})
	}

	jsonClass, err := json.Marshal(class)
	if err != nil {
		fmt.Print("json marshal failed")
		return
	}
	fmt.Printf("json:%s\n", jsonClass)
	fmt.Print(class)

	c1 := &Class{}
	err = json.Unmarshal([]byte(jsonClass), c1)
	if err != nil {
		fmt.Print("json unmarshal failed")
		return
	}
	fmt.Printf("%#v\n", c1)

	//Tag
	//Tag是结构体的元信息，可以在运行的时候通过反射的机制读取出来。
	//注意事项： 为结构体编写Tag时，必须严格遵守键值对的规则。
	//结构体标签的解析代码的容错能力很差，一旦格式写错，编译和运行时都不会提示任何错误，通过反射也无法正确取值。
	//Student 学生
	//type Student struct {
	//	ID     int    `json:"id"` //通过指定tag实现json序列化该字段时的key
	//	Gender string //json序列化是默认使用字段名作为key
	//	name   string //私有不能被json包访问
	//}

	//因为slice和map这两种数据类型都包含了指向底层数据的指针，因此我们在需要复制它们时要特别注意
	//在方法中使用传入的slice的拷贝进行结构体赋值。

}

func getPerson(name string, age int) *Person {
	return &Person{name: name, age: age}
}

func (p *Person) getName() string {
	return p.name
}

type Animal struct {
	name string
}

func (a *Animal) move() {
	fmt.Printf("%s会动！\n", a.name)
}

type Dog struct {
	Feet int8
	Animal
}

func (d *Dog) wang() {
	fmt.Printf("%s会汪汪汪~\n", d.name)
}
